/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#include "MaCIGUI2.hpp"
#include "DeviceLayer.h"

using namespace MaCIGUI;

DEFINE_EVENT_TYPE(MY_EVT_SERVICE_ASSIGNMENT)

BEGIN_EVENT_TABLE(CMaCIGUI2 , wxFrame)
    /*
    EVT_RADIOBOX(ID_RADIOBOX_MODE, CMaCIGUI2::OnMode)
    EVT_RADIOBOX(ID_RADIOBOX_POV, CMaCIGUI2::OnPOV)
    */
    EVT_CHECKBOX(ID_CHECKBOX_GRID, CMaCIGUI2::OnGridChecked)
    EVT_CHECKBOX(ID_CHECKBOX_FOLLOW, CMaCIGUI2::OnFollow)
    
    EVT_LISTBOX(ID_LIST_UNITS, CMaCIGUI2::OnSelectUnit)
    EVT_CHECKLISTBOX(ID_CHECKLIST_DEVICES, CMaCIGUI2::OnSelectDevice)
    EVT_LISTBOX(ID_CHECKLIST_DEVICES,CMaCIGUI2::OnSelectionOnDevice)
    EVT_TEXT_ENTER(ID_TEXTCTRL_GRID,CMaCIGUI2::OnGridSizeChange)

    
    EVT_BUTTON(ID_BUTTON_MAP,CMaCIGUI2::OnLoadMap)
    /*
    EVT_BUTTON(ID_BUTTON_TERRAIN,CMaCIGUI2::OnLoadTerrain)
    */
    EVT_CHILD_FOCUS(CMaCIGUI2::OnChildFocus)
  
    EVT_COMMAND(wxID_ANY, MY_EVT_SERVICE_ASSIGNMENT, CMaCIGUI2::OnServiceAssignmentWx)
    
  //EVT_KILL_FOCUS(CMaCIGUI::OnKillFocus)
  
    END_EVENT_TABLE()

CMaCIGUI2::CMaCIGUI2()
:wxFrame(NULL, wxID_ANY,"MaCIGUI - remake for Gimbo"),
 followMode(false),
 panel(this),
 canvas(this),
 machineUnitsCount(0),
 lastSelectedUnit(NULL),
 lastDeviceOnSelection(NULL),
 MaCICtrl(NULL)
{
  unitsMutex=ownMutex_Init();
  //make context stretch accross whole window
  wxBoxSizer *sizer=new wxBoxSizer(wxHORIZONTAL);
  sizer->Add(&canvas,1, wxALIGN_CENTRE | wxEXPAND,0);
  sizer->Add(&panel,0, wxEXPAND,0);
  SetSizer(sizer);
  Layout();


}

CMaCIGUI2::~CMaCIGUI2()
{
  /*
  delete clients
  delete thread
  */
  
  void *r;
  
  //terminate thread
  go=false;
  dPrint(1,"Terminating thread...");
  ownThread_Join(thread,&r);
  dPrint(1,"Thread terminated");
  std::vector<CMachineUnit*>::iterator i;
  dPrint(1,"Deleting units");
  for (i=units.begin();i!=units.end();i++)
  {
    delete (*i);
  }
  dPrint(1,"Deleted units");
  
}


void CMaCIGUI2::OnServiceAssignment(const Gimbo::TServiceAssignmentList &list)
{
  //std::map<std::string> testedNames;
  //dPrint(1,"1");
  Gimbo::TServiceAssignmentList::const_iterator si;
  for (si=list.begin(); si!=list.end(); si++)
  {
    
    if (!isAcceptedByMe( &(*si) ))
      continue;
    
    //dPrint(1,"2");
    std::string name;
    GetGimi()->findClientName(name,si->targetID,2000);
    MaCI::MaCICtrl::TServiceEntryArray services;
    MaCI::MaCICtrl::TServiceEntryArray::iterator iter;
    
    CMachineUnit* unit=NULL;
    
    int counter=0;
    
    bool found=false;
    
    assert(MaCICtrl != NULL);

    while (!found && counter<=5)
    {
      MaCICtrl->GetServiceArray(services,name,counter,2000);

      counter++;
      //for (iter=services.begin();iter!=services.end();iter++)
      if (services.size())
      {
        
        for (iter=services.begin();iter!=services.end();iter++)
        {
          if (iter->service.servicemajor == static_cast<unsigned int>(si->majorType) && 
              iter->service.serviceminor == static_cast<unsigned int>(si->minorType))
          {
            found=true;
            break;
          }
        }
      }
      // services.clear(); // FIXME: Removed by Zapo - clearing the list will invalidate the iterator!
    }
    
    
    if (!found)
    {
      dPrint(1,"No MaCI service discovered for assignment: %d:%d@%d",si->majorType,si->minorType,si->targetID);
      continue;
    }
    
    dPrint(1,"found name %s",name.c_str());
    const std::string &dg = iter->devicegroup;
    if (groupNames.insert(dg).second)
    {
      ownMutex_Lock(unitsMutex);
      //dPrint(1,"3");
      units.push_back(new CMachineUnit(dg,GetGimi()));
      canvas.AddMachineUnit(units.back());
      unit=units.back();
      
      machineUnitsCount++;
      
      //dPrint(1,"3.1");
      //update gui
      wxCommandEvent e(MY_EVT_SERVICE_ASSIGNMENT, GetId());
      
      TAssignmentEvent *ae=new TAssignmentEvent();
      ae->name=wxString(dg);
      ae->index=machineUnitsCount-1;
      ae->data=(void*)units.back();
      ae->deviceRefresh=false;
      
      e.SetClientData((void*)ae);
      
      AddPendingEvent(e);
      
      ownMutex_Unlock(unitsMutex);
      
      //dPrint(1,"3.2");
      //TODO: these should be done in the wx's main thread only...
      
      //dPrint(1,"3.4");
    } else {
      //dPrint(1,"4");
      std::vector<CMachineUnit*>::iterator i;

      for (i=units.begin();i!=units.end();i++)
      {
        if ((*i)->groupName == dg)
        {
          unit=*i;
          break;
        }
      }
    }
    ownMutex_Lock(unitsMutex);
    if (unit)
    {
      unit->AddService(*si,iter->GetMaCISL().instance);
      
      wxCommandEvent e(MY_EVT_SERVICE_ASSIGNMENT, GetId());
      TAssignmentEvent *ae=new TAssignmentEvent();
      ae->deviceRefresh=true;
      e.SetClientData((void*)ae);
      AddPendingEvent(e);
    }
    ownMutex_Unlock(unitsMutex);
    
  }
  /*
  wxArrayInt selections;
  if (panel.List_Units->GetSelections(selections))
  {
    
    wxCommandEvent e(wxEVT_COMMAND_LISTBOX_SELECTED,ID_LIST_UNITS);
    e.SetInt(selections[0]);
    AddPendingEvent(e);
  }*/
}

/** GIMUIComponent's functions*/

void CMaCIGUI2::OnConnect(gimi::GIMI *g)
{
  
  MaCICtrl=new MaCI::MaCICtrl::CMaCICtrlClient(g);
  
  if (MaCICtrl->Open()==MaCI::KMaCIOK)
  {
    
  } else {
    dPrint(1,"Couldn't open MaCICtrl client");
  }
  
  go=true;
  thread=ownThread_Create((void*)CMaCIGUI2::Entry,this);
}
// 
// void CMaCIGUI2::OnDisconnect()
// {
//   /*
//   suspend thread from polling gimi
//   */
// }

/** thread entry point */
void* CMaCIGUI2::Entry(void *param)
{
  /*
  update clientunits
  invoke repaint
  */

  CMaCIGUI2* mg=(CMaCIGUI2*)param;

  while (mg->go)
  {
    mg->updateMachineUnits();

    wxPaintEvent e;
    mg->canvas.AddPendingEvent(e);
    ownSleep_ms(100);
  }

  dPrint(1,"Thread ended")
  
      return NULL;
}

void CMaCIGUI2::initMachineUnits()
{
//   dPrint(1,"init units");
//   //search our services list to identify client names
//   std::set<std::string> names;
// 
//   std::vector<gimi::GIMIService>::iterator si;;
//   for (si=TargetServices.begin(); si!=TargetServices.end(); si++)
//   {
//     names.insert((*si).clientName);
//   }
// 
//   //create machineunit for every name
//   std::set<std::string>::iterator ni;
// 
//   for (ni=names.begin(); ni!=names.end(); ni++)
//   {
//     units.push_back(new CMachineUnit(*ni,Loader->GetGIMI()));
//     canvas.AddMachineUnit(units.back());
// 
//     wxString s(*ni);
//     panel.List_Units->InsertItems(1,&s,machineUnitsCount);
//     machineUnitsCount++;
//     panel.List_Units->SetClientData(machineUnitsCount-1,(void*)units.back() );
// 
//     dPrint(1,"set name to:%s",(*ni).c_str());
//     dPrint(1,"set data to %p",&(*(--(units.end()))));
//   }
}

void CMaCIGUI2::updateMachineUnits()
{
  std::vector<CMachineUnit*>::iterator i;

  ownMutex_Lock(unitsMutex);
  
  for (i=units.begin();i!=units.end();i++)
  {
    (*i)->Update();
  }
  //Perhaps it's in a wrong place....  update the view origin of canvas
  //TODO: is it in wrong place?
  if(followMode){
    if (lastSelectedUnit)
      canvas.SetViewOrigin(lastSelectedUnit->RefLayer->GetX(),lastSelectedUnit->RefLayer->GetY() );

  }
  ownMutex_Unlock(unitsMutex);
}

/** event handlers **/
void CMaCIGUI2::OnPOV(wxCommandEvent &e)
{
  if(e.GetSelection() == 0){
    followMode = false;
  }else if(e.GetSelection() == 1){
    followMode= true;
  }else
    dPrint(1,"WTF");
}

void CMaCIGUI2::OnMode(wxCommandEvent &e)
{

}

void CMaCIGUI2::OnFollow(wxCommandEvent &e)
{
  followMode=e.IsChecked();
}

void CMaCIGUI2::OnGridChecked(wxCommandEvent &e)
{
  double s;
  if (panel.TextCtrl_Grid->GetValue().ToDouble(&s))
  {
    canvas.SetGrid(e.IsChecked(),s);
  } else {
    panel.TextCtrl_Grid->SetValue("1.0");
    canvas.SetGrid(e.IsChecked(),1.0);
  }


}

void CMaCIGUI2::OnGridSizeChange(wxCommandEvent &e)
{
  double s;
  if (panel.TextCtrl_Grid->GetValue().ToDouble(&s))
  {
    canvas.SetGrid(panel.CheckBox_Grid->IsChecked(),s);
  } else {
    panel.TextCtrl_Grid->SetValue("1.0");
    canvas.SetGrid(panel.CheckBox_Grid->IsChecked(),1.0);
  }
}



void CMaCIGUI2::OnSelectUnit(wxCommandEvent &e)
{
  //dPrint(1,"asdadsa");
  CMachineUnit *mu=(CMachineUnit*)panel.List_Units->GetClientData(e.GetInt());
  //dPrint(1,"Unit %s selected, pointer %p.",mu->groupName.c_str(),mu);

  mu->Selected=true;
  canvas.SetSelectedUnit(mu);
  if (lastSelectedUnit && lastSelectedUnit!=mu)
    lastSelectedUnit->Selected=false;

  lastSelectedUnit=mu;

  //panel.CheckList_Devices->Clear();

  //int deviceCount=0;

  ownMutex_Lock(unitsMutex);
  /*
  std::vector<CDeviceLayer*>::iterator i;
  for (i=mu->DeviceLayers.begin(); i!=mu->DeviceLayers.end();i++)
  {
    wxString s((*i)->Name);
    panel.CheckList_Devices->InsertItems(1,&s,deviceCount);
    panel.CheckList_Devices->SetClientData(deviceCount,(void*)(*i));
    panel.CheckList_Devices->Check(deviceCount,(*i)->Enabled);
    deviceCount++;
  }*/
  refreshDevicesList();
  
  ownMutex_Unlock(unitsMutex);
}

void CMaCIGUI2::OnSelectionOnDevice(wxCommandEvent &e)
{
  if (e.IsSelection())
  {
    if(lastDeviceOnSelection)
      lastDeviceOnSelection->selectionOnDevice=!lastDeviceOnSelection->selectionOnDevice;


    CDeviceLayer *dl=(CDeviceLayer*)panel.CheckList_Devices->GetClientData(e.GetInt());
    //dPrint(1,"int=%d dl=%p",e.GetInt(),dl);
    dl->selectionOnDevice=!(dl->selectionOnDevice);
    lastDeviceOnSelection = dl;
    //dPrint(1,"Selection on device %s, enabled=%d", dl->Name.c_str(),dl->selectionOnDevice);
  }
}

void CMaCIGUI2::OnSelectDevice(wxCommandEvent &e)
{
  CDeviceLayer *dl=(CDeviceLayer*)panel.CheckList_Devices->GetClientData(e.GetInt());
  dl->Enabled=!dl->Enabled;//e.IsChecked();
  //dPrint(1,"selected device %s, enabled=%d", dl->Name.c_str(),dl->Enabled);

}

void CMaCIGUI2::OnLoadMap(wxCommandEvent &e)
{
  wxFileDialog fd(this,_T("Choose a map file"));

  if (fd.ShowModal()==wxID_OK)
  {
    canvas.SetMap(fd.GetPath().c_str());
  }
}

void CMaCIGUI2::OnLoadTerrain(wxCommandEvent &e)
{
  wxFileDialog fd(this,_T("Choose a terrain file"));

  if (fd.ShowModal()==wxID_OK)
  {
    canvas.SetTerrain(fd.GetPath().c_str());
  }
}

void CMaCIGUI2::OnChildFocus(wxChildFocusEvent &e)
{
  wxWindow *focused=wxWindow::FindFocus();
  
  //if focus is set to other than canvas, set it back
  if (focused!=&canvas && focused!=panel.TextCtrl_Grid)
  {
    canvas.SetFocus();
  }
  /*
  if (e.GetWindow()!=&canvas && e.GetWindow()!=panel.TextCtrl_Grid)
  {
    canvas.SetFocus();
  }*/
}

void CMaCIGUI2::OnKillFocus(wxFocusEvent &e)
{
  
}

void CMaCIGUI2::OnServiceAssignmentWx(wxCommandEvent& event)
{
  //dPrint(1,"Adding unit to list");
  
  TAssignmentEvent *ae=(TAssignmentEvent *)event.GetClientData();
  
  if (!(ae->deviceRefresh))
  {
    panel.List_Units->InsertItems(1,&(ae->name),ae->index);
  
    panel.List_Units->SetClientData(ae->index,ae->data );
  } else {
    refreshDevicesList();
  }
  delete ae;
  
}

bool CMaCIGUI2::isAcceptedByMe(const Gimbo::TServiceAssignment *si)
{
  int m=si->majorType;
  return (m==GIMI_PROTOCOL_MACI_RANGING ||
          m==GIMI_PROTOCOL_MACI_SPEEDCTRL ||
          m==GIMI_PROTOCOL_MACI_POSITION ||
          m==GIMI_PROTOCOL_MACI_COORDINATEDRIVE ||
          m==GIMI_PROTOCOL_MACI_MAP ||
          m==GIMI_PROTOCOL_MACI_ALARM ||
          m==GIMI_PROTOCOL_MACI_ENERGY );
  
}

void CMaCIGUI2::refreshDevicesList()
{
  if (lastSelectedUnit)
  {
    panel.CheckList_Devices->Clear();
  
    int deviceCount=0;
  
    //ownMutex_Lock(unitsMutex);
    
    std::vector<CDeviceLayer*>::iterator i;
    for (i=lastSelectedUnit->DeviceLayers.begin(); i!=lastSelectedUnit->DeviceLayers.end();i++)
    {
      wxString s((*i)->Name);
      panel.CheckList_Devices->InsertItems(1,&s,deviceCount);
      panel.CheckList_Devices->SetClientData(deviceCount,(void*)(*i));
      panel.CheckList_Devices->Check(deviceCount,(*i)->Enabled);
      deviceCount++;
    }
    //ownMutex_Unlock(unitsMutex);
  }
}

